package languageacquisition;

import jade.content.ContentManager;
import jade.content.lang.Codec;
import jade.content.lang.Codec.CodecException;
import jade.content.lang.sl.SLCodec;
import jade.content.onto.*;
import jade.core.Agent;
import jade.core.behaviours.*;
import jade.domain.FIPANames;
import jade.domain.FIPAAgentManagement.NotUnderstoodException;
import jade.domain.FIPAAgentManagement.RefuseException;
import jade.lang.acl.ACLMessage;
import jade.lang.acl.MessageTemplate;
import jade.proto.AchieveREResponder;

import java.security.acl.Acl;
import java.util.Collections;
import java.util.Random;
import languageacquisition.ontology.EnvObject;
import languageacquisition.ontology.HasState;
import languageacquisition.ontology.LanguageOntology;
import languageacquisition.ontology.Property;
import languageacquisition.ontology.State;

public class ObjectAgent extends ProjectAgent {

	/**
	 * Name of service type to be registered by an agent
	 */
	public static final String SERVICE_TYPE = "Observer";

	int desLength;
	int time = 0;
	double gamma;
	State state = new State();
	int period;
	EnvObject subject;
	Random random;

	private ContentManager manager = (ContentManager) getContentManager();

	private Codec codec = new SLCodec();
	private Ontology ontology = LanguageOntology.getInstance();

	public void setup() {

		// adapting ontology
		manager.registerLanguage(codec, FIPANames.ContentLanguage.FIPA_SL);

		manager.registerOntology(ontology);

		GetParameters();

		subject = new EnvObject(getAID());

		random = new Random();
		addBehaviour(new GenerateDescription());
		addBehaviour(new Register());
		
		addBehaviour(new UpdateDescription(this, period));

		MessageTemplate mt = MessageTemplate.and(MessageTemplate
				.MatchProtocol(FIPANames.InteractionProtocol.FIPA_QUERY),
				MessageTemplate.MatchPerformative(ACLMessage.QUERY_REF));
		addBehaviour(new ServiceReResponder(this, mt));
	}

	/**
	 * @author Michal Brzezinski-Spiczak Behaviour to register the existance of
	 *         the object on DF
	 */
	protected class Register extends OneShotBehaviour {
		public void action() {
			registerService(SERVICE_TYPE);
		}
	}

	/**
	 * method to get the parameters from agent start parameters: desLength,
	 * gamma, period
	 */
	protected void GetParameters() {

		desLength = (int) Integer.parseInt((String) getArguments()[0]);
		// length of features vector
		gamma = (double) Double.parseDouble((String) getArguments()[1]);// level
		// of envlability
		period = (int) Integer.parseInt((String) getArguments()[2]);// time
		// period to count the change in env

	}

	/**
	 * @author Michal Brzezinski-Spiczak Behaviour responsible for first
	 *         generate the description of the object
	 */
	protected class GenerateDescription extends OneShotBehaviour {
		public void action() {
			boolean added = false;
			for (int i = 0; i < desLength; i++) {
				double x = random.nextDouble();
				if (x > 0.95) {
					state.getProperties().add(new Property(i));
					added = true;
				}
			}
			if (!added) {
				state.getProperties().add(
						new Property(random.nextInt(desLength)));
			}
			// for (int i = 0; i < state.getProperties().size(); i++)
			// System.out
			// .print(((Property) state.getProperties().toArray()[i])
			// .getId()
			// + " | ");
			// System.out.println();
			
			/*
			String name = getLocalName();
	    	int id = (int)name.charAt(name.length()-1);
	    	int[] array = new int[10];
	    	for (int i=0; i<desLength; i++)
	    	{
	    		Property property = new Property(i);
	    		if (state.getProperties().contains(property))
	    			array[i] = 1;
	    		else
	    			array[i] = 0;
	    	}
			Visualization.updateDescription(id, array);
			*/
			
			time++;
		}
	}

	/**
	 * Behaviour responsible for the update/changes in environment
	 */
	protected class UpdateDescription extends TickerBehaviour {
		public UpdateDescription(Agent a, long period) {
			super(a, period);
		}

		protected void onTick() {
			// generating update to description
			for (int i = 0; i < desLength; i++) {
				double x = random.nextDouble();
				if (x > 1 - gamma) {
					boolean exist = false;
					int ind = -1;
					for (int k = 0; k < state.getProperties().size(); k++) {
						if (state.getProperties().get(k).getId() == i) {
							exist = true;
							ind = k;
							break;
						}
					}
					if (exist && state.getProperties().size() > 1)
						state.getProperties().remove(ind);
					else if (!exist) {
						state.getProperties().add(new Property(i));
					}
					Collections.sort(state.getProperties());
				}
			}
//			System.out.print(time + "\t:\t");
//			for (int i = 0; i < state.getProperties().size(); i++)
//				System.out
//						.print(((Property) state.getProperties().toArray()[i])
//								.getId()
//								+ ".");
//			System.out.println();
			String name = getLocalName();
	    	int id = (int)name.charAt(name.length()-1);
	    	int[] array = new int[10];
	    	for (int i=0; i<desLength; i++)
	    	{
	    		Property property = new Property(i);
	    		if (state.getProperties().contains(property))
	    			array[i] = 1;
	    		else
	    			array[i] = 0;
	    	}
			//Visualization.updateDescription(id, array);
			Visualization2.updateDescription(id, array);
			time++;
			
		}
	}

	protected class ServiceReResponder extends AchieveREResponder {
		public ServiceReResponder(Agent a, MessageTemplate mt) {
			super(a, mt);
		}

		protected EnvObject decodeQuery(Object query)
				throws NotUnderstoodException {

			return subject;
		}

		protected ACLMessage handleRequest(ACLMessage request)
				throws NotUnderstoodException, RefuseException {

			Object query;
			try {
				query = manager.extractAbsContent(request);
			} catch (CodecException e) {
				throw new NotUnderstoodException("CodecException: "
						+ e.getMessage());
			} catch (OntologyException e) {
				throw new NotUnderstoodException("OntologyException: "
						+ e.getMessage());
			}
			EnvObject queryObj = decodeQuery(query);

			if (!queryObj.equals(subject)) {
				throw new RefuseException("Object is not observed");
			}
			if (!subject.isObservable()) {
				throw new RefuseException("Object is currently not observable");
			} else
				return null;
		}

		protected ACLMessage prepareResultNotification(ACLMessage request,
				ACLMessage response) {
			ACLMessage result = request.createReply();
			result.setPerformative(ACLMessage.INFORM);
			try {
				result.setLanguage(FIPANames.ContentLanguage.FIPA_SL);
				result.setOntology(ontology.getName());
				manager.fillContent(result, getObservation());
			} catch (CodecException e) {
				e.printStackTrace();
			} catch (OntologyException e) {
				e.printStackTrace();
			}
			return result;
		}
	}

	public HasState getObservation() {
		HasState hasstate = new HasState();
		hasstate.setTime(time);
		hasstate.setObject(subject);
		hasstate.setState(state);
		return hasstate;
	}

}